package models;

import java.util.Collection;
import java.util.Random;
import java.util.Set;

import tools.HasInvariant;

/**
 * Version 3 of the hangman game implementation - where there is a good (?) OO
 * design
 */
public class Hangman3 extends Abstractions.HangmanAbstraction implements
		HasInvariant, Abstractions.HangmanSpecification {

	private WordStore words;

	public boolean invariant() {
		if (!words.hasWord(currentWord))
			return false;
		return super.invariant();
	}

	public Hangman3(String word) throws IllegalArgumentException {

		String[] oneword = { word };

		if (word == null)
			throw new IllegalArgumentException(
					"Cannot make a hangman game with no words");
		words = new WordStore(new Random(), oneword);
		randomWord();
	}

	public Hangman3(String[] strings) throws IllegalArgumentException {

		if (strings == null)
			throw new IllegalArgumentException(
					"Cannot make a hangman game with no words");
		words = new WordStore(new Random(), strings);
		randomWord();
	}

	public Hangman3(Random rng, String[] strings)
			throws IllegalArgumentException {

		if (strings == null)
			throw new IllegalArgumentException(
					"Cannot make a hangman game with no words");
		words = new WordStore(rng, strings);
		randomWord();
	}

	public Hangman3(Random rng, Set strings, boolean flag)
			throws IllegalArgumentException {

		if (strings == null)
			throw new IllegalArgumentException(
					"Cannot make a hangman game with no words");
		words = new WordStore(rng, strings);
		randomWord();
	}

	public Hangman3(Random rng, Collection<String> strings)
			throws IllegalArgumentException {

		if (strings == null)
			throw new IllegalArgumentException(
					"Cannot make a hangman game with no words");
		words = new WordStore(rng, strings);
		randomWord();
	}

	public String getCurrentWord() {
		return currentWord;
	}

	public boolean foundWord() {

		for (int i = 0; i < currentWord.length(); i++)
			if (!lettersFound[i])
				return false;
		return true;
	}

	public int triesLeft() {
		return ERROR_MAX - errorCount;
	}

	public boolean gameOver() {
		return foundWord() || triesLeft() <= 0;
	}

	public void randomWord() {
		currentWord = words.randomWord();
		errorCount = 0;
		won = false;
		lost = false;

		lettersFound = new boolean[currentWord.length()];
		for (int i = 0; i < currentWord.length(); i++) {
			lettersFound[i] = false;
		}

		for (int i = 0; i < ALPHABET_SIZE; i++) {
			lettersTried[i] = false;
		}
	}

	public void setRandomWordStr(String word) {
		currentWord = word;
		errorCount = 0;
		won = false;
		lost = false;

		lettersFound = new boolean[currentWord.length()];
		for (int i = 0; i < currentWord.length(); i++) {
			lettersFound[i] = false;
		}

		for (int i = 0; i < ALPHABET_SIZE; i++) {
			lettersTried[i] = false;
		}
	}

	public String randomWordStr() {
		currentWord = words.randomWord();
		errorCount = 0;
		won = false;
		lost = false;

		lettersFound = new boolean[currentWord.length()];
		for (int i = 0; i < currentWord.length(); i++) {
			lettersFound[i] = false;
		}

		for (int i = 0; i < ALPHABET_SIZE; i++) {
			lettersTried[i] = false;
		}
		return currentWord;
	}

	public String toString() {

		String str = "\nHangman -- Errors: " + errorCount + " from "
				+ ERROR_MAX;
		str = str + "\nCurrent Word: " + currentWord;
		str = str + "\nLetters Found: " + lettersInWord();
		str = str + "\nLetters Tried: " + lettersTried();
		str = str + "\nTriesLeft: " + triesLeft();
		str = str + "\nWon game is " + won;
		str = str + "\nLost game is " + lost;
		return str;
	}

	public String toString(String s) {

		String str = "\n" + s + "Hangman -- Errors: " + errorCount + " from "
				+ ERROR_MAX;
		str = str + "\n" + s + "Current Word: " + currentWord;
		str = str + "\n" + s + "Letters Found: " + lettersInWord();
		str = str + "\n" + s + "Letters Tried: " + lettersTried();
		str = str + "\n" + s + "TriesLeft: " + triesLeft();
		str = str + "\n" + s + "Won game is " + won;
		str = str + "\n" + s + "Lost game is " + lost;
		return str;
	}

	public String lettersTried() {
		String str = "";
		for (int i = 0; i < ALPHABET_SIZE; i++)

			if (lettersTried[i])
				str = str + ((char) ((int) ('a') + i));
			else
				str = str + "-";

		return str;
	}

	public String lettersInWord() {

		String str = "";

		for (int i = 0; i < currentWord.length(); i++) {
			if (lettersFound[i])
				str = str + currentWord.charAt(i);
			else
				str = str + '?';
		}
		return str;

	}

	public void tryLetter(char letter) throws IllegalStateException,
			IllegalArgumentException {

		if (won)
			throw new IllegalStateException(
					"Game is over: you already found the word");
		if (lost)
			throw new IllegalStateException(
					"Game is over: no more attempts left");
		if (letter < ALPHABET_FROM || letter > ALPHABET_TO)
			throw new IllegalArgumentException("Letters must be in alphabet");
		boolean found = false;
		int index = letter - (int) (ALPHABET_FROM);

		if (lettersTried[index])
			return;

		else {
			lettersTried[index] = true;
			for (int i = 0; i < currentWord.length(); i++)
				if (currentWord.charAt(i) == letter) {
					lettersFound[i] = true;
					found = true;
				}
			if (!found) {
				errorCount++;
				if (errorCount > ERROR_MAX)
					lost = true;
			}

		}
		won = foundWord();
		lost = (triesLeft() < 1);
	}

}
